package org.witsoft.dtx;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.witsoft.dtx.core.LocalTransaction;
import org.witsoft.dtx.core.LocalTransactionThreadLocal;
import org.witsoft.dtx.core.TransactionBranchHandler;
import org.witsoft.dtx.domain.BranchStatus;
import org.witsoft.dtx.domain.BranchTransaction;
import org.witsoft.dtx.domain.GlobalTransaction;
import org.witsoft.dtx.domain.GlobalStatus;
import org.witsoft.dtx.exception.DtxException;
import org.witsoft.dtx.service.BranchTransactionService;
import org.witsoft.dtx.service.GlobalTransactionService;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * @author XiaoQing Lin
 * @date 2020-09-18 18:37
 */
@Slf4j
public class LocalTransactionManager {

    @Autowired
    private BeanFactory beanFactory;
    @Autowired
    private GlobalTransactionService globalTxService;
    @Autowired
    private BranchTransactionService branchTxService;

    public void begin(int timeout){
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        if(transaction != null){
            LocalTransactionThreadLocal.remove();
//            throw new RuntimeException("事务已存在");
//            return;
        }
        String uuid = DigestUtils.md5DigestAsHex(UUID.randomUUID().toString().getBytes()).substring(8,23);
        GlobalTransaction globalTx = new GlobalTransaction(uuid);
        globalTx.setTimeout(timeout);
        globalTx.setStatus(GlobalStatus.PREPARE);
        // 插入数据库
        globalTxService.add(globalTx);
        // 设置当前事务
        transaction  = new LocalTransaction(globalTx);
        LocalTransactionThreadLocal.set(transaction);
    }

    public void begin(){
        begin(1000 * 60 * 10);
    }

    public void beginBranch(String branchId , Class<? extends TransactionBranchHandler> handlerClass, String handlerArgs){
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        String globalId  = transaction.getGlobal().getId();
        // 插入数据库
        BranchTransaction branchTx = new BranchTransaction(globalId, branchId);
        branchTx.setHandlerClass(handlerClass.getName());
        branchTx.setHandlerArgs(handlerArgs);
        branchTx.setStatus(BranchStatus.PREPARE);
        branchTxService.add(branchTx);
        // 加入分支事务
        transaction.addBranch(branchTx);
    }

    public void commitAsync(){
        boolean isSuccess = false;
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        // 更新数据库
        for (int i = 0; i < 5; i++) {
            try {
                GlobalTransaction globalTx = transaction.getGlobal();
                globalTx.setStatus(GlobalStatus.CONFIRMING);
                globalTxService.updateStatus(globalTx);
                isSuccess = true;
                break;
            }catch (Exception ex){
                log.error("事务(异步)提交失败" , ex);
            }
        }
        // 释放当前上下文
        LocalTransactionThreadLocal.remove();
        // 事务努力提交失败
        if(!isSuccess){
            throw new DtxException("事务多次(异步)提交失败");
        }
    }

    public void commitSync() throws Throwable{
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        int countSuccess  = 0;
        for (BranchTransaction branchTx : transaction.getBranches()) {
            try{
                String globalId = branchTx.getGid();
                TransactionBranchHandler handler = getHandler(branchTx.getHandlerClass());
                if(handler == null){
                    throw new DtxException("事务处理器类找不到:" + branchTx.getHandlerClass());
                }
                handler.confirm(globalId, branchTx.getBid(), branchTx.getHandlerArgs());
                // 更新数据库
                branchTx.setStatus(BranchStatus.CONFIRMED);
                branchTxService.updateStatus(branchTx);
                countSuccess ++;
            }catch (Throwable ex){
                log.error("分支事务(同步)确认失败", ex);
                throw ex;
            }
        }
        // 更新数据库
        GlobalTransaction globalTx = transaction.getGlobal();
        if(countSuccess >= transaction.getBranches().size()) {
            globalTx.setStatus(GlobalStatus.CONFIRMED);
            globalTxService.updateStatus(globalTx);
        }else{
            log.warn("事务(同步)确认失败,期待/成功:{}-{},{}", globalTx.getId(), transaction.getBranches().size(), countSuccess);
            throw new DtxException("事务部分确认失败:" + globalTx.getId());
        }
        // 释放当前上下文
        LocalTransactionThreadLocal.remove();
    }

    public void commit(boolean sync){
        if(sync){
            try {
                this.commitSync();
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            }
        }else{
            this.commitAsync();
        }
    }

    public void rollbackAsync(){
        boolean isSuccess = false;
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        for (int i = 0; i < 5; i++) {
            try {
                // 更新数据库
                GlobalTransaction globalTx = transaction.getGlobal();
                globalTx.setStatus(GlobalStatus.CANCELING);
                globalTxService.updateStatus(globalTx);
                isSuccess = true;
                break;
            }catch (Exception ex){
                log.error("事务(异步)回滚失败" , ex);
            }
        }
        // 释放当前上下文
        LocalTransactionThreadLocal.remove();
        // 事务努力提交失败
        if(!isSuccess){
            throw new DtxException("事务多次(异步)回滚失败");
        }
    }

    public void rollbackSync() throws Throwable{
        LocalTransaction transaction = LocalTransactionThreadLocal.get();
        // 是否马上放弃
        int countSuccess  = 0;
        for (BranchTransaction branchTx : transaction.getBranches()) {
            try{
                String globalId = branchTx.getGid();
                TransactionBranchHandler handler = getHandler(branchTx.getHandlerClass());
                if(handler == null){
                    throw new DtxException("事务处理器类找不到:" + branchTx.getHandlerClass());
                }
                handler.cancel(globalId, branchTx.getBid(), branchTx.getHandlerArgs());
                branchTx.setStatus(BranchStatus.CANCELED);
                branchTxService.updateStatus(branchTx);
                countSuccess ++;
            }catch (Throwable ex){
                log.error("分支事务(同步)取消失败", ex);
                throw ex;
            }
        }
        // 更新数据库
        GlobalTransaction globalTx = transaction.getGlobal();
        if(countSuccess >= transaction.getBranches().size()) {
            globalTx.setStatus(GlobalStatus.CANCELED);
            globalTxService.updateStatus(globalTx);
        }else{
            log.warn("事务(同步)取消失败,期待/成功:{}-{},{}", globalTx.getId(),  transaction.getBranches().size(), countSuccess);
            throw new DtxException("事务部分取消失败:" + globalTx.getId());
        }
        // 释放当前上下文
        LocalTransactionThreadLocal.remove();
    }


    public void rollback(boolean sync){
        if(sync){
            try {
                this.rollbackSync();
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            }
        }else{
            this.rollbackAsync();
        }
    }

    public void release(boolean sync){
        LocalTransaction localTransaction = LocalTransactionThreadLocal.get();
        if(localTransaction  != null){
            rollback(sync);
        }
    }

    private TransactionBranchHandler getHandler(String className){
        try {
            Class<TransactionBranchHandler> clz = (Class<TransactionBranchHandler>) Class.forName(className);
            return beanFactory.getBean(clz);
        } catch (ClassNotFoundException e) {
            return null;
        }
   }
}
